//https://blog.csdn.net/zhangskd/article/details/7496670
#ifndef CONTROL_VENO_HPP_
#define CONTROL_VENO_HPP_

#include <memory>
//#include <asm/div64.h>



#define V_PARAM_SHIFT 1

class CongestionControl_Veno : public CongestionControl
{
public:
  using ptr = std::unique_ptr<CongestionControl>;
  //virtual CongestionControl() {}

  //virtual ~CongestionControl() {}
  /**
   * @brief: slow start algorithm 
   * @param[in,out] cwnd 
   * @param[in,out] ssthresh
   */
  virtual void SlowStart(uint32_t &cwnd, uint32_t &ssthresh)
  {
      if (diff < beta){
        cwnd *= 3;
      }else{
        cwnd *= 2;
      }
  }
  /**
   * @brief: algorithm when packet loss occurs
   * @param[in,out] cwnd 
   * @param[in,out] ssthresh
   */
  virtual void Lost(uint32_t &cwnd, uint32_t &ssthresh)
  {
      if (diff < beta){
        cwnd;
        ssthresh;
      }else{
        cwnd = 1;
        ssthresh = std::max(ssthresh / 2,1u) ;
      }
  }
   /**
   * @brief: congesion avoidance algorithm 
   * @param[in,out] cwnd 
   * @param[in,out] ssthresh
   */
  virtual void CongestionAvoidance(uint32_t &cwnd, uint32_t &ssthresh)
  {
      uint64_t target_cwnd;
      uint32_t rtt;
      rtt = minrtt;
      target_cwnd = cwnd * basertt;
      target_cwnd <<= V_PARAM_SHIFT;
      target_cwnd = target_cwnd / rtt;
      /* 跟Vegas中diff的计算一样，只是这里乘2 */
      diff = (cwnd << V_PARAM_SHIFT) - target_cwnd;
      /* 处于拥塞避免阶段 */
      /* In the non-congestive state, increase cwnd every rtt。判断网络不处于拥塞，
         * 则cwnd的增长和Reno一样。
         */
      if (diff < beta) {
        cwnd = cwnd + 2;//说明是随机丢包而非拥塞丢包。
      } else{ //处于拥塞状态
          cwnd = cwnd + 1;
      }
      if (cwnd < 2)
          cwnd = 2;
      /* 重置minrtt，所以minrtt是每个ACK的RTT测量值*/
      minrtt = 0x7fffffff;
  }
  /**
   * @brief: quick recover algorithm 
   * @param[in,out] cwnd 
   * @param[in,out] ssthresh
   * @param[in]     fast_resend_trigger
   */
  virtual void QuickRecover(uint32_t &cwnd, uint32_t &ssthresh,uint32_t fast_resend_trigger)
  {
      //ssthresh  = std::max(ssthresh / 2,1u) ;
      //cwnd      = ssthresh + fast_resend_trigger;

      /* in non-congestive state, cut cwnd by 1/5，如果判断为随机丢包，那么慢启动
      * 阈值的减小幅度为0.2。
      */
      if (diff < beta){
        //ssthresh = std::max(cwnd * 4 / 5, 2U);
        ssthresh;
      } else{
        /* in congestive state, cut cwnd by 1/2，如果判断为拥塞丢包，那么慢启动阈值
         * 的减小幅度为0.5。
         */
        //ssthresh = std::max(cwnd >> 1U, 2U);
        ssthresh = std::max(cwnd * 4 / 5, 2U);
      }
      cwnd = ssthresh + fast_resend_trigger;
  }

  //在UpdateRto中调用即可
  /*在每收到ACK时调用，用于更新basertt和minrtt*/
  virtual void PacketAcked(uint32_t rtt)
  {
    uint32_t vrtt;

    if (rtt < 0)
        return;

    /* Never allow zero rtt or baseRTT */
    vrtt = rtt + 1;

    /* Filter to find propagation delay : */
    if (vrtt < basertt)
        basertt = vrtt;

    /* 和vegas的不一样，minrtt并不是上个RTT内的最小采样值，而是即时的rtt，
      * 因为每处理一个ACK后，minrtt都置为0x7fffffff。
      */
    minrtt = std::min(minrtt, vrtt);
    
  }

  virtual void SetRtt(uint32_t rtt){}

  virtual void SetMss(uint32_t mss){}

//veno
  const int beta = 5;
  const int n = 5;
  const int m = 2;
  /* 在Vegas中表示上个RTT内得到的RTT样本中的最小值，用于过滤delayed ACK，
   * 每个RTT才重置一次。而Veno却每个ACK重置一次，所以代表即时rtt。
   */
  uint32_t minrtt = 0x7fffffff;
  /* 连接的最小RTT，代表propagation delay。只有在连接刚建立或者idle后才重置，
   * 在其它情况下实时更新。
   */
  uint32_t basertt = 0x7fffffff;
  /* decide whether to increase cwnd，在拥塞避免阶段使用，当diff > 3时，在0和1转换，
   * 使cwnd两个RTT才加一。
   */
  uint32_t inc = 1;
  /* calculate the diff rate，计算网络瓶颈路由器中缓存的属于此连接的数据包的个数，
   * 是衡量网络是否处于拥塞状态的标准，从而判断丢包是随机丢包还是无线丢包，
   * 判断拥塞避免阶段是否应该放缓拥塞窗口增长速度。
   */
  uint32_t diff;

};


#endif
